package com.wyw.base.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.wyw.base.util.fileupload.RequestParser;
import com.wyw.core.exception.WYWException;

public class FileUploadUtil {
	
	final static Log log = LogFactory.getLog(FileUploadUtil.class);
	public static File UPLOAD_DIR = new File("e:/upload/uploads");
	private static File TEMP_DIR = new File("e:/upload/uploadsTemp");

	private static File[] getPartitionFiles(File directory, String filename) {
		File[] files = directory.listFiles(new PartitionFilesFilter(filename));
		Arrays.sort(files);
		return files;
	}

	public static void writeFileForMultipartRequest(RequestParser requestParser)
			throws Exception {
		if (requestParser.getPartIndex() >= 0) {
			writeFile(
					requestParser.getUploadItem().getInputStream(),
					new File(UPLOAD_DIR, requestParser.getUuid()+ "_"+ String.format("%05d",requestParser.getPartIndex())), null);

			if (requestParser.getTotalParts() - 1 == requestParser.getPartIndex()) {
				File[] parts = getPartitionFiles(UPLOAD_DIR,requestParser.getUuid());
				File outputFile = new File(UPLOAD_DIR,requestParser.getOriginalFilename());
				for (File part : parts) {
					mergeFiles(outputFile, part);
				}

				assertCombinedFileIsVaid(requestParser.getTotalFileSize(),
						outputFile, requestParser.getUuid());
				deletePartitionFiles(UPLOAD_DIR, requestParser.getUuid());
			}
		} else {
			writeFile(requestParser.getUploadItem().getInputStream(), new File(
					UPLOAD_DIR, requestParser.getFilename()), null);
		}
	}

	private static File writeFile(InputStream in, File out, Long expectedFileSize)
			throws IOException {
		FileOutputStream fos = null;

		try {
			fos = new FileOutputStream(out);

			IOUtils.copy(in, fos);

			if (expectedFileSize != null) {
				Long bytesWrittenToDisk = out.length();
				if (!expectedFileSize.equals(bytesWrittenToDisk)) {
					log.warn("Expected file  to be {" + expectedFileSize
							+ "} bytes; file on disk is {" + bytesWrittenToDisk
							+ "} bytes");
					out.delete();
					throw new IOException(
							String.format(
									"Unexpected file size mismatch. Actual bytes %s. Expected bytes %s.",
									bytesWrittenToDisk, expectedFileSize));
				}
			}

			return out;
		} catch (Exception e) {
			throw new IOException(e);
		} finally {
			IOUtils.closeQuietly(fos);
		}
	}

	private static void deletePartitionFiles(File directory, String filename) {
		File[] partFiles = getPartitionFiles(directory, filename);
		for (File partFile : partFiles) {
			partFile.delete();
		}
	}

	private static File mergeFiles(File outputFile, File partFile) throws Exception {
		FileOutputStream fos;
		FileInputStream fis;
		byte[] fileBytes;
		int bytesRead = 0;
		fos = new FileOutputStream(outputFile, true);
		fis = new FileInputStream(partFile);
		fileBytes = new byte[(int) partFile.length()];
		bytesRead = fis.read(fileBytes, 0, (int) partFile.length());
		assert (bytesRead == fileBytes.length);
		assert (bytesRead == (int) partFile.length());
		fos.write(fileBytes);
		fos.flush();
		fis.close();
		fos.close();

		return outputFile;
	}

	private static void assertCombinedFileIsVaid(int totalFileSize, File outputFile,
			String uuid) throws WYWException {
		if (totalFileSize != outputFile.length()) {
			deletePartitionFiles(UPLOAD_DIR, uuid);
			outputFile.delete();
			throw new WYWException("Incorrect combined file size!");
		}

	}

	
	private static class PartitionFilesFilter implements FilenameFilter {
		private String filename;

		PartitionFilesFilter(String filename) {
			this.filename = filename;
		}

		@Override
		public boolean accept(File file, String s) {
			return s.matches(Pattern.quote(filename) + "_\\d+");
		}
	}

}
